package com.ks.api.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ks.api.dto.KsEcpmReportDTO;
import com.ks.api.entity.KsConfig;
import com.ks.api.entity.KsEventData;
import com.ks.api.entity.KsConfigSc;
import com.ks.api.mapper.KsEventDataMapper;
import com.ks.api.mapper.KsConfigMapper;
import com.ks.api.mapper.KsConfigScMapper;
import com.ks.api.service.KsEventDataService;
import com.ks.api.service.KuaishouTokenService;
import com.ks.api.service.KsUserCostSummaryService;
import com.ks.api.utils.KuaishouApiUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 快手事件数据服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class KsEventDataServiceImpl extends ServiceImpl<KsEventDataMapper, KsEventData> implements KsEventDataService {

    private final KsEventDataMapper ksEventDataMapper;
    private final KuaishouApiUtils kuaishouApiUtils;
    private final KuaishouTokenService kuaishouTokenService;
    private final KsConfigMapper ksConfigMapper;
    private final KsConfigScMapper ksConfigScMapper;
    private final KsUserCostSummaryService ksUserCostSummaryService;
    
    @Value("${kuaishou.miniapp.advertiser_id}")
    private String advertiserId;
    
    @Value("${kuaishou.miniapp.appid}")
    private String appId;
    
    // 快手eCPM报表接口URL
    private static final String ECPM_REPORT_URL = "https://ad.e.kuaishou.com/rest/openapi/gw/dsp/v1/report/ecpm_report";
    
    // 测试用的openId列表，实际使用时应从配置中读取或动态获取
    private static final List<String> TEST_OPEN_IDS = Arrays.asList(
            "f11187870a1ca232814bf47248fe9ab9a",
            "f11187870a1ca232814bf47248fe9ab9b",
            "f11187870a1ca232814bf47248fe9ab9c",
            "f11187870a1ca232814bf47248fe9ab9d",
            "f11187870a1ca232814bf47248fe9ab9e",
            "f11187870a1ca232814bf47248fe9ab9f"
    );
    
    // 内存缓存，用于存储已处理过的事件ID，避免频繁查询数据库
    // key为eventId_openId_eventTime的组合
    private final Set<String> processedEventCache = ConcurrentHashMap.newKeySet();
    
    // 缓存容量限制，防止内存溢出
    private static final int MAX_CACHE_SIZE = 100000;
    
    // 缓存清理间隔时间（小时）
    private static final int CACHE_CLEAN_INTERVAL_HOURS = 24;
    
    // 最后一次成功获取数据的时间映射，按appId存储
    private final Map<String, Long> lastSuccessTimeMap = new ConcurrentHashMap<>();
    
    // token初始化检查间隔（秒）
    private static final int TOKEN_CHECK_INTERVAL = 5;
    
    // token初始化等待超时时间（秒）
    private static final int TOKEN_INIT_TIMEOUT = 300;
    
    // 标记token初始化是否完成
    private final AtomicBoolean tokenInitialized = new AtomicBoolean(false);
    
    /**
     * 初始化服务
     * 预加载已存在的事件数据到缓存
     */
    @PostConstruct
    public void init() {
        // 启动一个线程来检查token初始化状态
        new Thread(() -> {
            log.info("等待Token初始化完成...");
            
            long startTime = System.currentTimeMillis();
            boolean initSuccess = false;
            
            // 每5秒检查一次token是否可用，最多等待5分钟
            while (System.currentTimeMillis() - startTime < TOKEN_INIT_TIMEOUT * 1000L) {
                try {
                    if (kuaishouTokenService.isTokenInitialized()) {
                        tokenInitialized.set(true);
                        log.info("Token初始化完成，开始预加载事件缓存");
                        
                        // 预加载事件缓存
                        preloadEventCache();
                        
                        initSuccess = true;
                        break;
                    }
                    TimeUnit.SECONDS.sleep(TOKEN_CHECK_INTERVAL);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("等待Token初始化被中断", e);
                    break;
                } catch (Exception e) {
                    log.warn("等待Token初始化过程中发生异常: {}", e.getMessage());
                }
            }
            
            if (!initSuccess) {
                log.error("Token初始化等待超时（{}秒），ECPM数据获取可能会失败", TOKEN_INIT_TIMEOUT);
            }
        }, "EventDataInitCheck").start();
    }
    
    /**
     * 预加载最近的事件数据到缓存
     * 为了避免内存压力，仅加载最近1天的数据
     */
    private void preloadEventCache() {
        try {
            log.info("开始预加载事件缓存...");
            // 这里可以根据实际需要调整查询条件，例如只加载最近一天的数据
            List<KsEventData> recentEvents = ksEventDataMapper.findRecentEvents(DateUtil.offsetDay(new Date(), -1), 10000);
            
            int count = 0;
            for (KsEventData event : recentEvents) {
                if (event.getEventId() != null && event.getOpenId() != null && event.getEventTime() != null) {
                    String cacheKey = generateCacheKey(event.getEventId(), event.getOpenId(), event.getEventTime());
                    processedEventCache.add(cacheKey);
                    count++;
                }
            }
            
            log.info("预加载事件缓存完成，共加载{}条记录", count);
        } catch (Exception e) {
            log.error("预加载事件缓存失败", e);
        }
    }
    
    /**
     * 生成缓存Key
     */
    private String generateCacheKey(String eventId, String openId, String eventTime) {
        return eventId + "_" + openId + "_" + eventTime;
    }
    
    /**
     * 定期清理缓存
     * 每24小时执行一次
     */
    @Scheduled(fixedRate = CACHE_CLEAN_INTERVAL_HOURS, timeUnit = TimeUnit.HOURS)
    public void cleanEventCache() {
        log.info("开始清理事件缓存，当前缓存大小: {}", processedEventCache.size());
        
        // 如果缓存超过上限，清空缓存
        if (processedEventCache.size() > MAX_CACHE_SIZE) {
            processedEventCache.clear();
            log.info("缓存已清空");
            
            // 重新加载最近数据
            preloadEventCache();
        }
    }
    
    /**
     * 每秒执行一次，获取快手eCPM报表数据并保存
     * 只有在token初始化完成后才会真正执行
     */
    @Scheduled(fixedRate = 1000)
    public int fetchAndSaveEcpmReportData() {
        // 如果token还未初始化完成，静默跳过此次执行
        if (!tokenInitialized.get()) {
            return 0;
        }
        
        AtomicInteger totalSavedCount = new AtomicInteger(0);
        
        try {
            // 获取所有有效的配置
            List<KsConfigSc> configs = ksConfigScMapper.getAllKsConfigScs();
            
            // 遍历每个配置获取数据
            for (KsConfigSc config : configs) {
                String appId = config.getAppDh();
                String advertiserId = config.getAdvertiserId();
                
                // 检查时间间隔
                long currentTime = System.currentTimeMillis();
                long lastSuccessTime = lastSuccessTimeMap.getOrDefault(appId, 0L);
                long timeElapsed = currentTime - lastSuccessTime;
                
                // 只有当距离上次成功获取数据超过900毫秒时才执行
                if (lastSuccessTime > 0 && timeElapsed < 900) {
                    continue;
                }
                
                try {
                    // 构建请求参数
                    JSONObject params = new JSONObject();
                    params.set("advertiser_id", advertiserId);
                    params.set("app_id", config.getKfId());
                    params.set("page", 1);
                    params.set("page_size", 500);
                    params.set("data_hour", DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH"));
                    
                    // 使用Access-Token头发送请求获取数据
                    JSONObject result = kuaishouApiUtils.sendPostRequestWithAccessToken(ECPM_REPORT_URL, params, appId);
                    
                    if (result.getInt("code") == 0) {
                        // 记录最后一次成功获取数据的时间
                        lastSuccessTimeMap.put(appId, System.currentTimeMillis());
                        
                        // 解析返回数据
                        KsEcpmReportDTO reportDTO = JSONUtil.toBean(result.toString(), KsEcpmReportDTO.class);
                        if (reportDTO.getData() != null && reportDTO.getData().getDetails() != null && !reportDTO.getData().getDetails().isEmpty()) {
                            List<KsEventData> eventDataList = new ArrayList<>();
                            
                            // 处理返回的数据
                            for (KsEcpmReportDTO.DetailItem item : reportDTO.getData().getDetails()) {
                                // 数据校验
                                if (item.getOpenId() == null || item.getId() == null || item.getEventTime() == null) {
                                    log.warn("数据缺失，跳过记录: {}", JSONUtil.toJsonStr(item));
                                    continue;
                                }
                                
                                // 生成缓存Key
                                String cacheKey = generateCacheKey(item.getId(), item.getOpenId(), item.getEventTime());
                                
                                // 检查缓存和数据库
                                if (processedEventCache.contains(cacheKey) || 
                                    ksEventDataMapper.checkExists(item.getId(), item.getOpenId(), item.getEventTime()) > 0) {
                                    processedEventCache.add(cacheKey);
                                    continue;
                                }
                                
                                // 创建事件数据对象
                                KsEventData eventData = new KsEventData();
                                eventData.setOpenId(item.getOpenId());
                                eventData.setEventId(item.getId());
                                eventData.setEventTime(item.getEventTime());
                                eventData.setEventType("ecpm_report");
                                eventData.setCost(item.getCost() != null ? item.getCost() : BigDecimal.ZERO);
                                eventData.setStatus(0);
                                //eventData.setAppId(appId); // 添加appId字段
                                
                                // 保存额外数据
                                JSONObject extraData = new JSONObject();
                                extraData.set("cost", item.getCost());
                                extraData.set("request_id", reportDTO.getRequestId());
                                extraData.set("app_id", appId);
                                eventData.setEventData(extraData.toString());
                                
                                eventDataList.add(eventData);
                            }
                            
                            // 批量保存数据
                            if (!eventDataList.isEmpty()) {
                                boolean success = saveBatch(eventDataList);
                                if (success) {
                                    // 添加成功保存的记录到缓存
                                    eventDataList.forEach(event -> {
                                        String cacheKey = generateCacheKey(event.getEventId(), event.getOpenId(), event.getEventTime());
                                        processedEventCache.add(cacheKey);
                                    });
                                    
                                    totalSavedCount.addAndGet(eventDataList.size());
                                    log.info("成功保存 {} 条快手eCPM报表数据，应用ID: {}", eventDataList.size(), appId);
                                    
                                    // 触发成本汇总计算
                                    try {
                                        ksUserCostSummaryService.calculateAndSaveCostSummaries(eventDataList);
                                    } catch (Exception e) {
                                        log.error("计算用户成本汇总数据异常, appId: {}", appId, e);
                                    }
                                }
                            }
                        }
                    } else {
                        log.error("获取快手eCPM报表数据失败: appId={}, code={}, message={}", 
                                appId, result.getInt("code"), result.getStr("message"));
                    }
                } catch (Exception e) {
                    log.error("处理应用ID={}的数据时发生异常", appId, e);
                }
            }
        } catch (Exception e) {
            log.error("获取和保存快手eCPM报表数据异常", e);
        }
        
        return totalSavedCount.get();
    }
    
    @Override
    public boolean saveEventData(KsEventData eventData) {
        if (eventData == null || eventData.getEventId() == null || 
                eventData.getOpenId() == null || eventData.getEventTime() == null) {
            log.warn("事件数据缺失关键字段，无法保存");
            return false;
        }
        
        try {
            // 设置默认状态为未结算
            if (eventData.getStatus() == null) {
                eventData.setStatus(0);
            }
            
            // 生成缓存Key
            String cacheKey = generateCacheKey(eventData.getEventId(), eventData.getOpenId(), eventData.getEventTime());
            
            // 检查缓存
            if (processedEventCache.contains(cacheKey)) {
                log.info("事件数据已在缓存中，跳过保存: eventId={}, openId={}, eventTime={}", 
                        eventData.getEventId(), eventData.getOpenId(), eventData.getEventTime());
                return false;
            }
            
            // 检查数据库
            int exists = ksEventDataMapper.checkExists(
                    eventData.getEventId(), eventData.getOpenId(), eventData.getEventTime());
            
            if (exists > 0) {
                // 添加到缓存
                processedEventCache.add(cacheKey);
                log.info("事件数据已存在于数据库，添加到缓存: eventId={}, openId={}, eventTime={}", 
                        eventData.getEventId(), eventData.getOpenId(), eventData.getEventTime());
                return false;
            }
            
            // 保存数据
            boolean success = save(eventData);
            
            // 如果保存成功，添加到缓存
            if (success) {
                processedEventCache.add(cacheKey);
            }
            
            return success;
        } catch (Exception e) {
            log.error("保存事件数据异常", e);
            return false;
        }
    }
    
    @Override
    public Map<String, Object> findEventsByNickname(String nickname, String nickId, Integer page, Integer size) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 获取显示百分比配置
            KsConfig ksConfig = ksConfigMapper.getConfigValue("display_percent");
            String percentStr = ksConfig.getConfigValue();
            String topPriceStr = ksConfig.getTopPrice();
            double topPrice = topPriceStr != null ? Double.parseDouble(topPriceStr) : 0.5;
            BigDecimal topPriceBigDecimal = BigDecimal.valueOf(topPrice);
            double displayPercent = percentStr != null ? Double.parseDouble(percentStr) : 100.0;
            double multiplier = displayPercent / 100.0;
            
            // 构建分页对象
            Page<KsEventData> pageParam = new Page<>(page, size);
            
            // 调用Mapper执行查询
            IPage<KsEventData> eventPage = ksEventDataMapper.findEventsByNickname(pageParam, nickname, nickId);
            
            // 获取统计数据
            Map<String, Object> todayStats = calculateStats(
                ksEventDataMapper.getEventCostsByDateRange(nickname, nickId,
                    LocalDateTime.now().withHour(0).withMinute(0).withSecond(0),
                    LocalDateTime.now()),
                topPriceBigDecimal,
                multiplier
            );

            Map<String, Object> yesterdayStats = calculateStats(
                ksEventDataMapper.getEventCostsByDateRange(nickname, nickId,
                    LocalDateTime.now().minusDays(1).withHour(0).withMinute(0).withSecond(0),
                    LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)),
                topPriceBigDecimal,
                multiplier
            );
                
            Map<String, Object> weekStats = calculateStats(
                ksEventDataMapper.getEventCostsByDateRange(nickname, nickId,
                    LocalDateTime.now().minusWeeks(1),
                    LocalDateTime.now()),
                topPriceBigDecimal,
                multiplier
            );
                
            Map<String, Object> monthStats = calculateStats(
                ksEventDataMapper.getEventCostsByDateRange(nickname, nickId,
                    LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0),
                    LocalDateTime.now()),
                topPriceBigDecimal,
                multiplier
            );
            
            // 处理记录中的cost值（转换为元）
            if (eventPage.getRecords() != null) {
                eventPage.getRecords().forEach(event -> {
                    if (event.getCost() != null) {
                        // 将厘转换为元，并应用显示百分比
                        BigDecimal yuanValue = event.getCost()
                                .divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP)
                                .multiply(BigDecimal.valueOf(multiplier));
                        if (yuanValue.compareTo(topPriceBigDecimal) > 0) {
                            yuanValue = topPriceBigDecimal;
                        }
                        event.setCost(yuanValue);
                    }
                });
            }
            
            // 封装返回结果
            result.put("total", eventPage.getTotal());
            result.put("pages", eventPage.getPages());
            result.put("current", eventPage.getCurrent());
            result.put("size", eventPage.getSize());
            result.put("records", eventPage.getRecords());
            result.put("displayPercent", displayPercent);
            
            // 添加统计数据
            Map<String, Object> stats = new HashMap<>();
            stats.put("today", todayStats);
            stats.put("yesterday", yesterdayStats);
            stats.put("week", weekStats);
            stats.put("month", monthStats);
            result.put("stats", stats);
            
        } catch (Exception e) {
            log.error("查询事件数据失败", e);
            throw new RuntimeException("查询事件数据失败: " + e.getMessage());
        }
        
        return result;
    }
    
    /**
     * 计算统计数据
     * @param costs 原始cost列表（单位：厘）
     * @param topPrice 最大限额（单位：元）
     * @param multiplier 显示百分比
     * @return 统计结果
     */
    private Map<String, Object> calculateStats(List<BigDecimal> costs, BigDecimal topPrice, double multiplier) {
        Map<String, Object> stats = new HashMap<>();
        
        if (costs == null || costs.isEmpty()) {
            stats.put("count", 0);
            stats.put("total_cost", BigDecimal.ZERO);
            return stats;
        }
        
        stats.put("count", costs.size());
        
        BigDecimal totalCost = costs.stream()
            .map(cost -> {
                // 将厘转换为元
                BigDecimal yuanValue = cost.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP);
                BigDecimal multipliedValue = yuanValue.multiply(BigDecimal.valueOf(multiplier));
                // 应用最大值限制
                if (multipliedValue.compareTo(topPrice) > 0) {
                    return topPrice;
                }
                // 应用显示百分比
                return multipliedValue;
            })
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .setScale(2, RoundingMode.HALF_UP);
        
        stats.put("total_cost", totalCost);
        return stats;
    }

    /**
     * 获取所有事件数据（分页）
     */
    @Override
    public Map<String, Object> findAllEvents(Integer page, Integer size, String startTime, String endTime, String nickId, String nickname) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 获取显示百分比配置
            KsConfig ksConfig = ksConfigMapper.getConfigValue("display_percent");
            String percentStr = ksConfig.getConfigValue();
            String topPriceStr = ksConfig.getTopPrice();
            double topPrice = topPriceStr != null ? Double.parseDouble(topPriceStr) : 0.5;
            BigDecimal topPriceBigDecimal = BigDecimal.valueOf(topPrice);
            double displayPercent = percentStr != null ? Double.parseDouble(percentStr) : 100.0;
            double multiplier = displayPercent / 100.0;
            
            // 构建分页对象
            Page<KsEventData> pageParam = new Page<>(page, size);
            
            // 调用Mapper执行查询
            IPage<KsEventData> eventPage = ksEventDataMapper.findAllEvents(pageParam, nickId, nickname, startTime, endTime);
            
            // 获取统计数据
            Map<String, Object> todayStats = calculateStats(
                ksEventDataMapper.getAllEventCostsByDateRange(
                    DateUtil.format(DateUtil.beginOfDay(new Date()), "yyyy-MM-dd HH:mm:ss"),
                    DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"),
                    nickId,
                    nickname),
                topPriceBigDecimal,
                multiplier
            );

            Map<String, Object> yesterdayStats = calculateStats(
                ksEventDataMapper.getAllEventCostsByDateRange(
                    DateUtil.format(DateUtil.beginOfDay(DateUtil.yesterday()), "yyyy-MM-dd HH:mm:ss"),
                    DateUtil.format(DateUtil.endOfDay(DateUtil.yesterday()), "yyyy-MM-dd HH:mm:ss"),
                    nickId,
                    nickname),
                topPriceBigDecimal,
                multiplier
            );
                
            Map<String, Object> weekStats = calculateStats(
                ksEventDataMapper.getAllEventCostsByDateRange(
                    DateUtil.format(DateUtil.beginOfWeek(new Date()), "yyyy-MM-dd HH:mm:ss"),
                    DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"),
                    nickId,
                    nickname),
                topPriceBigDecimal,
                multiplier
            );
                
            Map<String, Object> monthStats = calculateStats(
                ksEventDataMapper.getAllEventCostsByDateRange(
                    DateUtil.format(DateUtil.beginOfMonth(new Date()), "yyyy-MM-dd HH:mm:ss"),
                    DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"),
                    nickId,
                    nickname),
                topPriceBigDecimal,
                multiplier
            );
            
            // 处理记录中的cost值（转换为元）和用户状态
            if (eventPage.getRecords() != null) {
                eventPage.getRecords().forEach(event -> {
                    if (event.getCost() != null) {
                        // 将厘转换为元，并应用显示百分比
                        BigDecimal yuanValue = event.getCost()
                                .divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP)
                                .multiply(BigDecimal.valueOf(multiplier));
                        if (yuanValue.compareTo(topPriceBigDecimal) > 0) {
                            yuanValue = topPriceBigDecimal;
                        }
                        event.setCost(yuanValue);
                    }
                    
                    // 处理用户状态：null和1都是正常，0是封禁
                    if (event.getUserStatus() == null || event.getUserStatus() == 1) {
                        event.setUserStatus(1); // 设置为正常
                    }
                });
            }
            
            // 封装返回结果
            result.put("total", eventPage.getTotal());
            result.put("pages", eventPage.getPages());
            result.put("current", eventPage.getCurrent());
            result.put("size", eventPage.getSize());
            result.put("records", eventPage.getRecords());
            result.put("displayPercent", displayPercent);
            
            // 添加统计数据
            Map<String, Object> stats = new HashMap<>();
            stats.put("today", todayStats);
            stats.put("yesterday", yesterdayStats);
            stats.put("week", weekStats);
            stats.put("month", monthStats);
            result.put("stats", stats);
            
        } catch (Exception e) {
            log.error("查询所有事件数据失败", e);
            throw new RuntimeException("查询所有事件数据失败: " + e.getMessage());
        }
        
        return result;
    }
    
    @Override
    public int updateUserStatus(String nickId, int status) {
        if (nickId == null || nickId.trim().isEmpty()) {
            throw new IllegalArgumentException("用户编号不能为空");
        }
        if (status != 0 && status != 1) {
            throw new IllegalArgumentException("无效的状态值");
        }
        return ksEventDataMapper.updateUserStatus(nickId, status);
    }
    
    @Override
    public int updateEventStatus(String nickId, String startTime, String endTime) {
        if (nickId == null || nickId.trim().isEmpty()) {
            throw new IllegalArgumentException("用户编号不能为空");
        }
        if (startTime == null || startTime.trim().isEmpty() || endTime == null || endTime.trim().isEmpty()) {
            throw new IllegalArgumentException("时间范围不能为空");
        }
        return ksEventDataMapper.updateEventStatus(nickId, startTime, endTime);
    }
} 